/**
 * 常用工具方法
 * Copyright(c) 2015 huanjie
 */

/*
 * 严格模式
 * */
'use strict'


/*
 * 导出
 * */
module.exports = {
    guid: guid,
    miniGuid: miniGuid,
    midGuid: midGuid,
    dateFormat: dateFormat,
    dateParse: dateParse,
    extend: extend,
    getPropertyValue: getPropertyValue,
    isArray: isArray,
    isNull: isNull,
    isUndefined: isUndefined,
    isNullOrUndefined: isNullOrUndefined,
    isString: isString,
    isFunction: isFunction,
    isObject: isObject,
    isDate: isDate,
    isRegExp: isRegExp,
    isNumber: isNumber,
    isBoolean: isBoolean,
    isError: isError,
    inherits: inherits,
    liuco: liuco,
    basename: basename,
    extname: extname,
    minExtend: minExtend
};


/*
* 创建一个guid
* es6中可以使用symbol，但是这东西如果存入数据库\读取是一个什么效果呢？
* */
function guid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
};

/**
 * 生成一个uid，长度只有8位。
*/
function miniGuid() {
    return 'xxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
};

/**
 * 生成一个uid, 长度12位。
 * @param {boolean} upper 是否转换成大写
 */
function midGuid(upper) {
    return 'xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return upper ? v.toString(16).toUpperCase() : v.toString(16);
    });
}

/*
 * Date对象转为字符串
 * */
function dateFormat(date, format) {
    if (!date || !date.getMonth || !date.getSeconds) {
        return "";
    }
    var fmt = format || "yyyy-MM-dd HH:mm:ss";
    var o = {
        "M+": date.getMonth() + 1,                              //月份   
        "d+": date.getDate(),                                   //日   
        "H+": date.getHours(),                                  //小时   
        "m+": date.getMinutes(),                                //分钟   
        "s+": date.getSeconds(),                                //秒   
        "q+": Math.floor((date.getMonth() + 3) / 3),            //季度   
        "S": date.getMilliseconds()                             //毫秒   
    };
    if (/(y+)/.test(fmt))
        fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
        if (new RegExp("(" + k + ")").test(fmt))
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
};

/*
 * 字符串解析为时间
 * */
function dateParse(date, format) {
    // 年
    var yearExp = /y{1,4}/;
    var year = dateItemParse(yearExp, format, date);
    if (year < 100) {
        // 两位年数，默认为20xx年，所以此方法仅在本世纪有效
        year = parseInt("20" + year);
    }

    // 月
    var monthExp = /MM/;
    var month = dateItemParse(monthExp, format, date) - 1;      // 月份的表示默认加1，所以需要减去
    // 日
    var dayExp = /dd/;
    var day = dateItemParse(dayExp, format, date);
    //  小时-- 24С小时制
    var hourExp = /HH/;
    var hour = dateItemParse(hourExp, format, date);
    // 分
    var minExp = /mm/;
    var min = dateItemParse(minExp, format, date);
    // 秒
    var secondExp = /ss/;
    var second = dateItemParse(secondExp, format, date);
    // 返回时间对象
    return new Date(year, month, day, hour, min, second);
};
/*
 * 字符串解析为时间辅助方法
 * 根据正则解析[年，月，日，时，分，秒]中的具体一项。
 * */
function dateItemParse(exp, format, date) {
    var item = exp.exec(format);
    if (item && item.length === 1) {
        var length = item[0].length;
        var itemValue = date.slice(item.index, item.index + length);
        return parseInt(itemValue);
    }
    return 0;
};

/*
 * 将target对象的属性合并到ori对象上
 * 只适用于简单对象
 * */
function extend(ori, target) {
    if (ori && target) {
        for (var key in target) {
            ori[key] = target[key];
        };
    }
    return ori;
};

/**
 * 
 * @param {*目标对象} ori  
 * @param {*数据来源对象} target 
 * @param {*排除的属性项} exkey 
 */
function minExtend(ori, target, exkey) {
    if (ori && target) {
        for (var key in target) {
            if (key !== exkey) {
                ori[key] = target[key];
            }
        };
    }
    return ori;
}

/**
 * 继承
 *  从nodejs中拷贝
*/
function inherits(ctor, superCtor) {
    if (typeof Object.create === "function") {
        // 支持ES5的浏览器
        ctor.super_ = superCtor;
        ctor.prototype = Object.create(superCtor.prototype, {
            constructor: {
                value: ctor,
                enumerable: false,
                writable: true,
                configurable: true
            }
        });
    }
    else {
        // 兼容老浏览器
        ctor.super_ = superCtor;
        var TempCtor = function () { };
        TempCtor.prototype = superCtor.prototype;
        ctor.prototype = new TempCtor();
        ctor.prototype.constructor = ctor;
    }
};


/**
 * 获取对象的属性字段值，支持嵌套
 * 使用.号分割
*/
function getPropertyValue(val, proname) {
    if (isNullOrUndefined(val)) {
        return null;
    }
    if (isNullOrUndefined(proname)) {
        return val;
    }
    var pros = proname.split(".");
    var proval = val;
    var index = 0;
    while (proval && index < pros.length) {
        proval = proval[pros[index]];
        index++;
    }
    return proval;
};


/*
* 对象是否为数组
* */
function isArray(val) {
    return Array.isArray(val);
};

/**
 *  判断对象是否为空
 *  */
function isNull(val) {
    return val === null;
};

/**
 * 判断对象是否未定义
 */
function isUndefined(val) {
    return val === undefined;
};

/**
 * 对象是否为空或者未定义
 */
function isNullOrUndefined(val) {
    return val === null || val === undefined;
};

/**
 * 对象是否为字符串
 */
function isString(val) {
    return typeof val === "string";
};
/**
 * 对象是否为数字
 */
function isNumber(val) {
    return typeof val === "number";
};

/**
 * 对象是否为方法(函数)
 */
function isFunction(val) {
    return typeof val === "function";
};
/**
 * 对象是否为布尔值
 */
function isBoolean(val) {
    return typeof val === "boolean";
};

/**
 * 对象是否为对象(此处对象为数据类型)
 */
function isObject(val) {
    return !isNullOrUndefined(val) && typeof val === "object";
};
/**
 * 判断对象是否为时间对象
 */
function isDate(val) {
    return objectToString(val) === "[object Date]";
};

/**
 * 对象是否为正则表达式
 */
function isRegExp(val) {
    return objectToString(val) === "[object RegExp]";
};

/**
 * 对象是否为异常类型实例
 */
function isError(val) {
    return objectToString(val) === "[object Error]" || val instanceof Error;
};

/**
 * 对象转成字符串
 * 由于方法的调用发生在Object对象上，所以返回结果都是传入对象的类型字符串。
 */
function objectToString(val) {
    return Object.prototype.toString.call(val);
};

/**
 * 获取文件路径中的文件名
 */
function basename(path, ext) {
    if (ext !== undefined && typeof ext !== 'string') {
        return "";
    }
    var start = 0;
    var end = -1;
    var matchedSlash = true;
    var i;

    // Check for a drive letter prefix so as not to mistake the following
    // path separator as an extra separator at the end of the path that can be
    // disregarded
    if (path.length >= 2) {
        const drive = path.charCodeAt(0);
        if ((drive >= 65/*A*/ && drive <= 90/*Z*/) ||
            (drive >= 97/*a*/ && drive <= 122/*z*/)) {
            if (path.charCodeAt(1) === 58/*:*/)
                start = 2;
        }
    }

    if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
        if (ext.length === path.length && ext === path)
            return '';
        var extIdx = ext.length - 1;
        var firstNonSlashEnd = -1;
        for (i = path.length - 1; i >= start; --i) {
            const code = path.charCodeAt(i);
            if (code === 47/*/*/ || code === 92/*\*/) {
                // If we reached a path separator that was not part of a set of path
                // separators at the end of the string, stop now
                if (!matchedSlash) {
                    start = i + 1;
                    break;
                }
            } else {
                if (firstNonSlashEnd === -1) {
                    // We saw the first non-path separator, remember this index in case
                    // we need it if the extension ends up not matching
                    matchedSlash = false;
                    firstNonSlashEnd = i + 1;
                }
                if (extIdx >= 0) {
                    // Try to match the explicit extension
                    if (code === ext.charCodeAt(extIdx)) {
                        if (--extIdx === -1) {
                            // We matched the extension, so mark this as the end of our path
                            // component
                            end = i;
                        }
                    } else {
                        // Extension does not match, so our result is the entire path
                        // component
                        extIdx = -1;
                        end = firstNonSlashEnd;
                    }
                }
            }
        }

        if (start === end)
            end = firstNonSlashEnd;
        else if (end === -1)
            end = path.length;
        return path.slice(start, end);
    } else {
        for (i = path.length - 1; i >= start; --i) {
            const code = path.charCodeAt(i);
            if (code === 47/*/*/ || code === 92/*\*/) {
                // If we reached a path separator that was not part of a set of path
                // separators at the end of the string, stop now
                if (!matchedSlash) {
                    start = i + 1;
                    break;
                }
            } else if (end === -1) {
                // We saw the first non-path separator, mark this as the end of our
                // path component
                matchedSlash = false;
                end = i + 1;
            }
        }

        if (end === -1)
            return '';
        return path.slice(start, end);
    }
};

/**
 * 获取文件后缀名
 */
function extname(path) {
    var startDot = -1;
    var startPart = 0;
    var end = -1;
    var matchedSlash = true;
    // Track the state of characters (if any) we see before our first dot and
    // after any path separator we find
    var preDotState = 0;
    for (var i = path.length - 1; i >= 0; --i) {
        const code = path.charCodeAt(i);
        if (code === 47/*/*/ || code === 92/*\*/) {
            // If we reached a path separator that was not part of a set of path
            // separators at the end of the string, stop now
            if (!matchedSlash) {
                startPart = i + 1;
                break;
            }
            continue;
        }
        if (end === -1) {
            // We saw the first non-path separator, mark this as the end of our
            // extension
            matchedSlash = false;
            end = i + 1;
        }
        if (code === 46/*.*/) {
            // If this is our first dot, mark it as the start of our extension
            if (startDot === -1)
                startDot = i;
            else if (preDotState !== 1)
                preDotState = 1;
        } else if (startDot !== -1) {
            // We saw a non-dot and non-path separator before our dot, so we should
            // have a good chance at having a non-empty extension
            preDotState = -1;
        }
    }

    if (startDot === -1 ||
        end === -1 ||
        // We saw a non-dot character immediately before the dot
        preDotState === 0 ||
        // The (right-most) trimmed path component is exactly '..'
        (preDotState === 1 &&
            startDot === end - 1 &&
            startDot === startPart + 1)) {
        return '';
    }
    return path.slice(startDot, end);
};



/**
 * 采用函数生成器进行流程控制
 * 模仿TJ大神的co库
 */
function liuco(generator) {
    return new Promise(function (resolve, reject) {
        var gen = generator();
        function next(err, result) {
            if (err) {              // 异常
                reject(err);
            }
            var step = gen.next(result);
            if (step.done) {        // 结束
                resolve(step.value || result);
            }
            else {                  // 继续执行
                step.value.then(function (val) {
                    next(null, val);
                }).catch(function (err) {
                    reject(err);
                });
            }
        }
        next();
    });
};
